package com.example.fuel.slice;

import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import ohos.bundle.IBundleManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import com.example.fuel.FileUtils;
import com.example.fuel.ResourceTable;
import com.example.fuel.rxutil.OhosSchedulers;
import com.github.kittinunf.fuel.Fuel;
import com.github.kittinunf.fuel.FuelKt;
import com.github.kittinunf.fuel.core.FileDataPart;
import com.github.kittinunf.fuel.core.FuelError;
import com.github.kittinunf.fuel.core.FuelManager;
import com.github.kittinunf.fuel.core.Method;
import com.github.kittinunf.fuel.core.Request;
import com.github.kittinunf.fuel.core.Response;
import com.github.kittinunf.fuel.core.ResponseDeserializable;
import com.github.kittinunf.fuel.core.extensions.AuthenticatedRequest;
import com.github.kittinunf.fuel.core.requests.CancellableRequest;
import com.github.kittinunf.fuel.core.requests.DownloadRequest;
import com.github.kittinunf.fuel.core.requests.UploadRequest;
import com.github.kittinunf.fuel.rx.RxFuelKt;
import com.github.kittinunf.fuel.stetho.StethoHook;
import com.github.kittinunf.result.Result;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;

import org.jetbrains.annotations.NotNull;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import io.reactivex.SingleObserver;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.functions.Function3;
import kotlin.text.Charsets;

/**
 * MainAbilitySlice
 *
 * @since 2021-05-29
 */
public class MainAbilitySlice extends AbilitySlice {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0x00201, "MY_TAG");
    private static final String NULLSTR = "";
    private static final String FUEL_ISSUES_1 = "https://api.github.com/repos/kittinunf/Fuel/issues/1";
    private static final String BASE_PATH = "http://httpbin.org";
    private static final String KITTINUNF_FUEL_ISSUES = "https://api.github.com/repos/kittinunf/Fuel/issues";
    Text textDisc;
    Text mainAuxText;
    Text loadView;

    CompositeDisposable bag;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        loadView = (Text) findComponentById(ResourceTable.Id_loadView);
        textDisc = (Text) findComponentById(ResourceTable.Id_text_disc);
        mainAuxText = (Text) findComponentById(ResourceTable.Id_mainAuxText);
        textDisc.setText(NULLSTR);
        Button button = (Button) findComponentById(ResourceTable.Id_btGo);
        button.setClickedListener((component) -> {
            if (!isLoad()) {
                execute();
            }

        });

        findComponentById(ResourceTable.Id_btGoRoutines).setClickedListener(component -> {
            if (!isLoad()) {
                executeCoroutine();
            }
        });

        findComponentById(ResourceTable.Id_btGoClean).setClickedListener(component -> {
            if (!isLoad()) {
                textDisc.setText(NULLSTR);
                mainAuxText.setText(NULLSTR);
                dismissLoadView();
            }
        });
        initHttp();
    }

    private boolean isLoad() {
        return loadView.getVisibility() == Component.VISIBLE;
    }

    private void initHttp() {
        bag = new CompositeDisposable();
        FuelManager.Companion.getInstance().setBasePath(BASE_PATH);

        Map map = new HashMap();
        map.put("Device", "ohos");
        FuelManager.Companion.getInstance().setBaseHeaders(map);

        List<Pair<String, String>> list = new ArrayList<>();

        list.add(new Pair<>("key", "value"));
        FuelManager.Companion.getInstance().setBaseParams(list);
        FuelManager.Companion.getInstance().setHook(new StethoHook("Fuel Sample App"));
    }

    private void execute() {
        showLoadView();
        httpGet();
        httpPut();
        httpPost();
        httpPatch();
        httpDelete();
        httpDownload();
        httpUpload();
        httpBasicAuthentication();
        httpListResponseObject();
        httpResponseObject();
        httpGsonResponseObject();
        httpCancel();
        httpRxSupport();
    }

    private void executeCoroutine() {
        showLoadView();
        httpGetCoroutine();
    }

    private void showLoadView() {
        loadView.setVisibility(Component.VISIBLE);
    }

    private void dismissLoadView() {
        loadView.setVisibility(Component.INVISIBLE);
    }

    private void httpGetCoroutine() {
        List<Pair<String, String>> list = new ArrayList<>();
        list.add(new Pair<>("userId", "123"));
        Request request = Fuel.INSTANCE.get("/get", list);

        HiLog.info(LABEL, "start");
        request.responseString(new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                getUITaskDispatcher().asyncDispatch(() -> {
                    dismissLoadView();
                });
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpGet() {
        Request request = Fuel.INSTANCE.get("/get", getPairList());
        request.responseString(new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });

        FuelKt.httpGet("/get", null).responseString(
            new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
                @Override
                public Unit invoke(Request request, Response response, Result<String, ?
                    extends FuelError> stringResult) {
                    updateText(stringResult.toString());
                    Optional<Unit> optionalUnit = Optional.empty();
                    return optionalUnit.get();
                }
            });
    }


    private void httpPut() {
        Request request = Fuel.INSTANCE.put("/put", getPairList());
        request.responseString(new Function3<Request, Response,
            Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });

        FuelKt.httpPut("/put", getPairList()).responseString(new Function3<Request, Response,
            Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpPost() {
        Request request = Fuel.INSTANCE.post("/post", getPairList());
        request.responseString(new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });

        FuelKt.httpPost("/post", getPairList()).responseString(new Function3<Request,
            Response, Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpPatch() {
        FuelManager.Companion.getInstance().setBasePath(BASE_PATH);

        Map map = new HashMap();
        map.put("Device", "ohos");
        FuelManager.Companion.getInstance().setBaseHeaders(map);

        List<Pair<String, String>> list = new ArrayList<>();
        list.add(new Pair<>("key", "value"));
        FuelManager.Companion.getInstance().setBaseParams(list);
        FuelManager.Companion.getInstance().setForceMethods(true);

        Request request = FuelManager.Companion.getInstance().request(Method.PATCH, "/patch", getPairList());
        request.responseString(new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpDelete() {
        Request request = Fuel.INSTANCE.delete("/delete", getPairList());

        request.responseString(new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });

        FuelKt.httpDelete("/delete", getPairList()).responseString(new Function3<Request, Response,
            Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpDownload() {
        int num = 100;
        DownloadRequest downloadRequest = Fuel.INSTANCE.download("/bytes/" + 1024 * num, Method.GET, null);
        downloadRequest.fileDestination(new Function2<Response, Request, File>() {
            @Override
            public File invoke(Response response, Request request) {
                File file = new File(FileUtils.getSavePath(getContext(), new Date().getTime() + "test.tmp"));
                return file;
            }
        });
        downloadRequest.progress(new Function2<Long, Long, Unit>() {
            @Override
            public Unit invoke(Long aLong, Long aLong2) {
                getUITaskDispatcher().asyncDispatch(() -> {
                    mainAuxText.setText(aLong + "/" + aLong2);
                });
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
        downloadRequest.responseString(new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                getUITaskDispatcher().asyncDispatch(() -> {
                    dismissLoadView();
                });
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpUpload() {
        UploadRequest uploadRequest = Fuel.INSTANCE.upload("/post", Method.POST, null);
        File file = new File(FileUtils.getSavePath(getContext(), "out.tmp"));
        file = writeFile(file);

        String name = getFileNameWithoutExtention(file.getName());
        String contentDisposition = "form-data; name=" + "; filename=" + file.getName();
        FileDataPart fileDataPart = new FileDataPart(file, name, file.getName(),
            FileDataPart.Companion.guessContentType(file), contentDisposition);
        uploadRequest.add(fileDataPart);
        uploadRequest.responseString(new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });

        uploadRequest.progress(new Function2<Long, Long, Unit>() {
            @Override
            public Unit invoke(Long aLong, Long aLong2) {
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }


    private void httpBasicAuthentication() {
        String username = getString(ResourceTable.String_mainability_name);
        String password = getString(ResourceTable.String_mainability_pwd);

        Request request = Fuel.INSTANCE.get("/basic-auth/" + username + "/" + password, null);
        AuthenticatedRequest authenticatedRequest = new AuthenticatedRequest(request);
        authenticatedRequest.basic(username, password);
        authenticatedRequest.responseString(new Function3<Request, Response,
            Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });

        Request request2 = FuelKt.httpGet("/basic-auth/" + username + "/" + password, null);
        AuthenticatedRequest authenticatedRequest2 = new AuthenticatedRequest(request2);
        authenticatedRequest2.basic(username, password);
        authenticatedRequest2.responseString(new Function3<Request, Response,
            Result<String, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<String, ? extends FuelError> stringResult) {
                updateText(stringResult.toString());
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpListResponseObject() {
        Request request = FuelKt.httpGet(KITTINUNF_FUEL_ISSUES, null);
        ListDeserializer listDeserializer = new ListDeserializer();
        request.responseObject(listDeserializer, new Function3<Request, Response,
            Result<? extends Object, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response,Result<? extends Object, ?
                extends FuelError> stringResult) {
                Gson gson = new Gson();
                String jsonStr = gson.toJson(stringResult);
                JsonObject returnObj = new JsonParser().parse(jsonStr).getAsJsonObject();
                JsonArray value = returnObj.get("value").getAsJsonArray();
                String result = gson.toJson(value);

                Type type = new TypeToken<List<Issue>>() {
                }.getType();
                List<Issue> issueList = gson.fromJson(result, type);
                StringBuffer stringBuffer = new StringBuffer();
                for (Issue issue : issueList) {
                    String str = ", title=" + issue.title + ", url=" + issue.url + "),";
                    stringBuffer.append("Issue(id=" + issue.id + str);
                }
                updateText("[" + stringBuffer.toString() + "]");
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpResponseObject() {
        Request request = FuelKt.httpGet(FUEL_ISSUES_1, null);
        Deserializer deserializer = new Deserializer();
        request.responseObject(deserializer, new Function3<Request, Response,
            Result<? extends Object, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response,Result<? extends
                Object, ? extends FuelError> stringResult) {
                Gson gson = new Gson();
                String jsonStr = gson.toJson(stringResult);
                JsonObject returnObj = new JsonParser().parse(jsonStr).getAsJsonObject();
                JsonObject value = returnObj.get("value").getAsJsonObject();
                String result = gson.toJson(value);

                Issue issue = gson.fromJson(result, Issue.class);
                String str = ", title=" + issue.title + ", url=" + issue.url + ")";
                updateText("Success: " + "Issue(id=" + issue.id + str);
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpGsonResponseObject() {
        Request request = FuelKt.httpGet(FUEL_ISSUES_1, null);
        request.responseString(new Function3<Request, Response, Result<? extends Object, ? extends FuelError>, Unit>() {
            @Override
            public Unit invoke(Request request, Response response, Result<?
                extends Object, ? extends FuelError> stringResult) {
                Gson gson = new Gson();
                String jsonStr = gson.toJson(stringResult);
                JsonObject returnObj = new JsonParser().parse(jsonStr).getAsJsonObject();
                String value = returnObj.get("value").getAsString();
                JsonObject jsonObject = new JsonParser().parse(value).getAsJsonObject();
                String id = jsonObject.get("id").getAsString();
                String url = jsonObject.get("url").getAsString();
                String title = jsonObject.get("title").getAsString();

                updateText("Success: " + "Issue(id=" + id + ", title=" + title + ", url=" + url + ")");
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
    }

    private void httpCancel() {
        Request request = Fuel.INSTANCE.get("/delay/10", null);
        request.interrupt(new Function1<Request, Unit>() {
            @Override
            public Unit invoke(Request request) {
                HiLog.info(LABEL, request.getUrl().getPath() + " is interrupted");
                Optional<Unit> optionalUnit = Optional.empty();
                return optionalUnit.get();
            }
        });
        CancellableRequest cancellableRequest = request.responseString(
            new Function3<Request, Response, Result<String, ? extends FuelError>, Unit>() {
                @Override
                public Unit invoke(Request request, Response response, Result<String, ?
                    extends FuelError> stringResult) {
                    /* noop */
                    Optional<Unit> optionalUnit = Optional.empty();
                    return optionalUnit.get();
                }
            });
        getUITaskDispatcher().asyncDispatch(() -> {
            cancellableRequest.cancel();
        });
    }

    private void httpRxSupport() {
        Request request = FuelKt.httpGet(FUEL_ISSUES_1, null);
        RxFuelKt.rxObject(request, new Deserializer())
            .subscribeOn(Schedulers.newThread())
            .observeOn(OhosSchedulers.mainThread())
            .subscribe(new SingleObserver<Result<Object, FuelError>>() {
                @Override
                public void onSubscribe(@NotNull Disposable disposable) {
                    if (disposable != null) {
                        bag.add(disposable);
                    }
                }

                @Override
                public void onSuccess(@NotNull Result<Object, FuelError> objectFuelErrorResult) {
                    Gson gson = new Gson();
                    String jsonStr = gson.toJson(objectFuelErrorResult);
                    JsonObject value = null;
                    try {
                        JsonObject returnObj = new JsonParser().parse(jsonStr).getAsJsonObject();
                        value = returnObj.get("value").getAsJsonObject();

                        String result = gson.toJson(value);

                        Issue issue = gson.fromJson(result, Issue.class);
                        String str = "Issue(id=" + issue.id + ", title=" + issue.title + ", url=" + issue.url + ")";
                        HiLog.info(LABEL, "Success: " + str);
                    } catch (IllegalStateException exception) {
                        exception.printStackTrace();
                    }
                }

                @Override
                public void onError(@NotNull Throwable throwable) {
                }
            });
    }


    private void updateText(String result) {
        getUITaskDispatcher().asyncDispatch(() -> {
            textDisc.append(result);
        });
    }

    private List<Pair<String, String>> getPairList() {
        List<Pair<String, String>> list = new ArrayList<>();
        list.add(new Pair<>("foo", "foo"));
        list.add(new Pair<>("bar", "bar"));
        return list;
    }

    private File writeFile(File file) {
        FileOutputStream in = null;
        try {
            if (!file.exists()) {
                boolean isOk = file.createNewFile();
                if (!isOk) {
                    HiLog.info(LABEL, "create fail");
                }
            }
            in = new FileOutputStream(file);
            for (int i = 0; i < 100; i++) {
                byte[] bytes = "abcdefghijklmnopqrstuvwxyz".getBytes();
                in.write(bytes, 0, bytes.length);
            }

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }

        return file;
    }

    /**
     * getFileNameWithoutExtention
     *
     * @param str fileFullName
     * @return String str
     */
    public static String getFileNameWithoutExtention(String str) {
        String fileFullName = str;
        // 截断路径分隔符.
        int backslash = fileFullName.lastIndexOf("\\") != -1 ? fileFullName.lastIndexOf("\\") :
            fileFullName.lastIndexOf("/");
        int dot = fileFullName.lastIndexOf(".");
        if (backslash > 0 && dot > 0) {
            fileFullName = fileFullName.substring(backslash + 1, dot);
        }
        return fileFullName;
    }

    /**
     * Issue
     *
     * @since 2021-06-10
     */
    public final class Issue {
        private final int id;
        private final String title;
        private final String url;

        public int getId() {
            return this.id;
        }

        public String getTitle() {
            return this.title;
        }

        public String getUrl() {
            return this.url;
        }

        public Issue(int id, @NotNull String title, @NotNull String url) {
            this.id = id;
            this.title = title;
            this.url = url;
        }
    }

    /**
     * Deserializer
     *
     * @since 2021-06-10
     */
    public static final class Deserializer implements ResponseDeserializable<Object> {
        @Override
        public Object deserialize(@NotNull Response response) {
            InputStream stream = response.getBody$fuel().toStream();

            Reader reader = new InputStreamReader(stream, Charsets.UTF_8);
            Issue issue = (Issue) deserialize(reader);
            if (issue != null) {
                return issue;
            }
            return response;
        }

        @Override
        public Object deserialize(@NotNull InputStream inputStream) {
            return inputStream;
        }

        @Override
        public Object deserialize(@NotNull Reader reader) {
            Issue issue = (new Gson()).fromJson(reader, Issue.class);
            return issue;
        }

        @Override
        public Object deserialize(@NotNull byte[] bytes) {
            return bytes;
        }

        @Override
        public Object deserialize(@NotNull String str) {
            return str;
        }
    }

    /**
     * ListDeserializer
     *
     * @since 2021-06-10
     */
    public static final class ListDeserializer implements ResponseDeserializable<Object> {
        @Override
        public Object deserialize(@NotNull Response response) {
            InputStream stream = response.getBody$fuel().toStream();
            Reader reader = new InputStreamReader(stream, Charsets.UTF_8);
            List<Issue> issue = (List<Issue>) deserialize(reader);
            if (issue != null) {
                return issue;
            }
            return response;
        }

        @Override
        public Object deserialize(@NotNull InputStream inputStream) {
            return inputStream;
        }

        @Override
        public Object deserialize(@NotNull Reader reader) {
            Type type = new TypeToken<List<Issue>>() {
            }.getType();
            List<Issue> issues = new Gson().fromJson(reader, type);
            return issues;
        }

        @Override
        public Object deserialize(@NotNull byte[] bytes) {
            return bytes;
        }

        @Override
        public Object deserialize(@NotNull String str) {
            return str;
        }
    }
}
